package cn.bonoon.kernel.menus;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import cn.bonoon.kernel.util.MD5Util;
import cn.bonoon.kernel.util.StringHelper;

/**
 * 这个是在配置文件里定义的一个帮助文档项，允许定义多个；
 * 也允许使用路径，路径只匹配key，并且路径的父路径的部分不处理，只处理路径最终所指向的文件或文件夹
 * 
 * @author jackson
 *
 */
public class DescribeDocument {
	
	/** 这个名称是在配置文件里定义的名称，如果帮助文档上已经定义了名称的，则使用帮助文档上的名称；否则使用这里的名称 */
	private final String name;
	
	private final String[] paths;
	private long lastModified = Long.MIN_VALUE;
	
	/** 是否是相对路径，如果是，则会自动组父类的路径 */
	private final boolean absolute;
	
	public DescribeDocument(String name, String path) {
		this.name = name;
		String tem = path;
		this.absolute = tem.startsWith("/");
		if(absolute) tem = tem.substring(1);
		this.paths = tem.split("/");
	}
	
	private DescribeContext describeContext;
	private DescribeDefinitionImpl describe;

	public void init(DescribeContext describeContext, DescribeDefinitionImpl describe){
		this.describeContext = describeContext;
		this.describe = describe;
	}
	
	private Map<String, DescribeDocumentFile> cached = new HashMap<>(8);
	public void resolve(File document, List<DescribeDocumentFile> documentFiles) throws Exception{
		long lastModified = document.lastModified();
		if(this.lastModified == lastModified){
			documentFiles.addAll(cached.values());
			return;//没有改变过，不需要重新读取
		}
		
		//重新解析
		cached.clear();
		List<File> candidates = new ArrayList<>();
		internalReadCandidates(document, 0, candidates);
		internalResolveFiles(candidates.toArray(new File[candidates.size()]), documentFiles);
		
		this.lastModified = lastModified; 
	}
	
	private void internalReadCandidates(File parent, int index, List<File> candidates){
		String key = paths[index];
		
		for(File file : parent.listFiles()){
			if(!check(file, key)) continue;
			
			int nextIndex = index + 1;
			if(paths.length > nextIndex){
				if(file.isDirectory()){
					internalReadCandidates(file, nextIndex, candidates);
				}
				
				continue;
			}
				
			candidates.add(file);
		}
	}
	
	private boolean check(File file, String key){
		String fname = file.getName();
		
		if(fname.startsWith(key)){
			if(fname.length() == key.length()) return true;
			char c = fname.charAt(key.length());
			return c == '@' || c == '.';
		}
		
		return false;
	}
	
	private void internalResolveFiles(File[] files, List<DescribeDocumentFile> documentFiles){
		/*
		 * 如果是".htm"或".html"格式的，可以在源码里使用图片、样式、js等文件；
		 * 这些资源文件都放在对应的 "文件名+.files" 文件夹下面；
		 */
		List<File> candidateFolders = new ArrayList<>();
		List<String> filenames = new ArrayList<>();
		for(int i = 0; i < files.length; i++){
			File file = files[i];
			String fname = file.getName();

			if(fname.startsWith("-")) continue;
			
			if(file.isDirectory()){
				candidateFolders.add(file);
			}else if(file.isFile()){
				//读取文件
				InternalDescribeDocument idDoc = internalResolveFile(file, documentFiles);
				if(null != idDoc){
					documentFiles.add(idDoc);
					cached.put(idDoc.key, idDoc);
					filenames.add(idDoc.resourceFolder());
				}
			}
		}
		
		for(File folder : candidateFolders){
			if(!filenames.contains(folder.getPath())){
				internalResolveFiles(folder.listFiles(), documentFiles);
			}
		}
	}

	private InternalDescribeDocument internalResolveFile(File file, List<DescribeDocumentFile> documentFiles){
		String filename = file.getName();
		int dividePoint  = filename.indexOf('@');
		int nameStartOf;
		if(dividePoint == -1){
			nameStartOf = dividePoint = 0;
		}else{
			nameStartOf = dividePoint + 1;
		}
		int endPoint = filename.indexOf('.', dividePoint);//因为可能是后缀，所以这个必须有的
		if(endPoint == -1){
			//只定义了一个名称，后缀什么的都没有。是不允许这种情况出现的
			return null;
		}
		
		String name = null, version = "";
		
		if(endPoint > nameStartOf){
			name = filename.substring(nameStartOf, endPoint);
		}
		
		int suffixPoint = filename.lastIndexOf('.');
		if(suffixPoint > endPoint){
			version = filename.substring(endPoint + 1, suffixPoint);
		}
		
		name = StringHelper.get(name, this.name);
		String suffix = filename.substring(suffixPoint);
		return new InternalDescribeDocument(file, name, version, suffix);
	}
	
	private DescribeDocumentBuilder loadBuilder(InternalDescribeDocument idDoc){
		if(isHtml(idDoc)){
			return new HtmlDescribeDocumentBuilder(idDoc);
		}else if(isVideo(idDoc)){
			return new VideoDescribeDocumentBuilder(idDoc);
		}
		return new DownloadDescribeDocumentBuilder(idDoc);
	}
	
	private boolean isHtml(InternalDescribeDocument idDoc){
		return htmls.contains(idDoc.suffix);
	}
	
	private static final List<String> htmls = new ArrayList<>();
	private static final List<String> videos = new ArrayList<>();
	static{
		htmls.add(".html");
		htmls.add(".htm");
		
		videos.add(".mp4");
		videos.add(".avi");
		videos.add(".rm");
		videos.add(".mp3");
		videos.add(".flv");
		videos.add(".mkv");
	}
	private boolean isVideo(InternalDescribeDocument idDoc){
		return videos.contains(idDoc.suffix);
	}
	
	private abstract class AbstractDescribeDocumentBuilder implements DescribeDocumentBuilder{
		
		protected final InternalDescribeDocument idDocument;
		private final StringBuilder html = new StringBuilder();
		
		AbstractDescribeDocumentBuilder(InternalDescribeDocument idDocument){
			this.idDocument = idDocument;
		}
		
		@Override
		public final StringBuilder build(){
			html.append("[");
			html.append(StringHelper.date2String(idDocument.file.lastModified()));
			html.append("] ");
			internalItemBuild(html);

			if(StringHelper.isNotEmpty(idDocument.version)){
				html.append(" - [版本：");
				html.append(idDocument.version);
				html.append("]");
			}
			
			internalContentBuild(html);
			return html;
		}
		
		protected void internalItemBuild(StringBuilder html){}
		
		protected void internalContentBuild(StringBuilder html){}
	}
	
	private class VideoDescribeDocumentBuilder extends AbstractDescribeDocumentBuilder{
		
		VideoDescribeDocumentBuilder(InternalDescribeDocument idDocument){
			super(idDocument);
		}

		@Override
		protected void internalItemBuild(StringBuilder html) {
			html.append("<a href='javascript:void(null);' title='观看视频：");
			html.append(idDocument.name);
			html.append("' onclick='jQuery(\"#");
			html.append(idDocument.key);
			html.append("\").slideToggle();'>");
			html.append(idDocument.name);
			html.append("[视频]</a>");
		}
		
		@Override
		protected void internalContentBuild(StringBuilder html)  {
			
			html.append("<div id='");
			html.append(idDocument.key);
			html.append("' style='display:none;'>");
			html.append("<EMBED style=' margin: 5px auto; width:900px; height: 400px'; src='");
			html.append(describeContext.getVideoUrl());
			html.append("?mid=").append(describe.getParent().getId());
			html.append("&key=").append(idDocument.key);
			//html.append("http://player.youku.com/player.php/sid/XNDU4MjM3Mjg=/v.swf");
			html.append("' quality= 'high'   ");
			html.append("wmode='transparent'  ");
			html.append("pluginspage='http://www.macromedia.com/go/getflashplayer'");
			html.append("type='application/x-shockwave-flash'>  ");
			html.append("</div>");
		}
	}

	private class DownloadDescribeDocumentBuilder extends AbstractDescribeDocumentBuilder{
		
		DownloadDescribeDocumentBuilder(InternalDescribeDocument idDocument){
			super(idDocument);
		}
		
		@Override
		protected void internalItemBuild(StringBuilder html) {
			html.append("<a href='");
			html.append(describeContext.getDownloadUrl());
			html.append("?mid=").append(describe.getParent().getId());
			html.append("&key=").append(idDocument.key);
			html.append("' target='_blank' title='下载：");
			html.append(idDocument.name);
			html.append("'>");
			html.append(idDocument.name);
			html.append("[下载]</a>");
		}
	}
	
	private class HtmlDescribeDocumentBuilder extends AbstractDescribeDocumentBuilder{
		

		HtmlDescribeDocumentBuilder(InternalDescribeDocument idDocument){
			super(idDocument);
		}

		@Override
		protected void internalItemBuild(StringBuilder html) {
			html.append("<a href='javascript:void(null);' title='查看：");
			html.append(idDocument.name);
			html.append("' onclick='jQuery(\"#");
			html.append(idDocument.key);
			html.append("\").slideToggle();'>");
			html.append(idDocument.name);
			html.append("</a>");
		}
		
		@Override
		protected void internalContentBuild(StringBuilder html)  {
			
			html.append("<div id='").append(idDocument.key).append("' style='display:none;'>");
			StringBuilder stringBuilder = new StringBuilder();
			try(BufferedReader reader = new BufferedReader(new FileReader(idDocument.file))){
				String line = null;
				while ((line = reader.readLine()) != null) {
					stringBuilder.append(line);
				}
			}catch (Exception e) {
				e.printStackTrace();
				html.append(e.getMessage());
			}
			html.append(stringBuilder);
			html.append("</div>");
		}
	}
	
	private class InternalDescribeDocument implements DescribeDocumentFile{
		
		/** 这里是从帮助文档上解析到的名称，如果文档上没有定义名称，则使用{@link DescribeDocument#name}的定义 */
		private final String name;
		/** 帮助文档的版本 */
		private final String version;
		/** 帮助文档的后缀，支持.doc,.htm,.avi等格式 */
		private final String suffix;
		
		private final File file;
		
		final String key;
		
		private StringBuilder html;
		
		@Override
		public String getFilename() {
			return name + suffix;
		}
		
		@Override
		public String getSuffix() {
			return suffix;
		}
		
		@Override
		public File getFile() {
			return file;
		}
		
		InternalDescribeDocument(File file, String name, String version, String suffix){
			this.file = file;
			this.name = name;
			this.version = version;
			this.suffix = suffix.toLowerCase();
			
			this.key = MD5Util.randomMD5String();
			
		}
		
		@Override
		public String toString() {
			if(null == html){
				DescribeDocumentBuilder builder = loadBuilder(this);
				
				html = builder.build();
			}
			return html.toString();
		}
		
		/**
		 * 资源文件的路径，如：
		 * <pre>
		 * 文件：c:/a/b/11-22@中文说明.v1.0.htm
		 * 那么默认定义的资源文件夹为
		 * 文件夹：c:/a/b/11-22@中文说明.v1.0.files
		 * </pre>
		 */
		String resourceFolder(){
			String path = file.getPath();
			int len = path.length() - suffix.length();
			return path.substring(0, len) + ".files";
		}
	}
	
	public boolean isAbsolute() {
		return absolute;
	}
	
	public DescribeDefinition getDescribe() {
		return describe;
	}
	
	public DescribeContext getDescribeContext() {
		return describeContext;
	}

	public DescribeDocumentFile get(String key) {
		return cached.get(key);
	}
}
